在上一篇文章 kintone 外掛開發 ④ 簡單實作範例 part 2 - 製作外掛設定畫面 中,我們已經完成了外掛設定畫面的實作,讓使用者可以透過直覺的 UI 介面,自行選擇應用程式中的欄位並進行相對應的設定。
本篇將進一步說明,如何透過 kintone plugin API 將使用者所設定的內容儲存起來,並在實際應用程式中讀取這些設定,使外掛功能能夠根據這些資料正確運作。
在 kintone 提供的 JavaScript API 中,目前有下列與外掛相關的 API:
接下來的實作中,我們將會使用 kintone.plugin.app.setConfig()
來儲存外掛設定,並透過 kintone.plugin.app.getConfig()
讀取先前儲存的設定內容。
kintone.plugin.app.setConfig(config, successCallback)
參數
參數名稱 | 類型 | 是否必填 | 說明 |
---|---|---|---|
config |
物件 | 必填 | 儲存於外掛的設定資料。格式為鍵值對,例如:{ "key1": "value1", "key2": "value2" } 。※ key 為 ASCII 相容字元,value 為字串。 |
successCallback |
函式 | 可省略 | 儲存成功後執行的函式,無參數。若省略,系統將自動導回外掛清單畫面並顯示設定完成訊息。若指定此函式,則不會自動跳轉。 |
由於 config
的值僅支援字串格式,若需要儲存陣列、物件等資料類型,請先使用 JSON.stringify()
將其轉為字串,讀取時再透過 JSON.parse()
還原為原始格式。
可使用的畫面
kintone.plugin.app.getConfig(pluginId)
參數
參數名稱 | 類型 | 是否必填 | 說明 |
---|---|---|---|
pluginId |
字串 | 必填 | 要取得其設定資訊的外掛ID |
可使用的畫面
在進入外掛設定畫面時,通常會透過 getConfig()
來載入並填入先前儲存的設定資料,讓使用者可進行編輯;而在應用程式中,則須在客製化程式中引用這些設定內容,取代原先寫死的欄位代碼或其他固定值,使外掛能根據實際設定運作。
首先,為「保存」按鈕加上點擊事件的監聽器,點擊按鈕時可以先透過 console.log
檢視目前的 data
資料內容:
// 按鈕功能
saveButton.addEventListener('click', () => {
console.log(data)
})
如上圖所示,在表格中新增三筆設定資料,點擊保存後會列印出一個陣列,包含每一列的設定物件。接下來,我們要透過 kintone.plugin.app.setConfig()
將這些設定資料儲存到 kintone:
// 按鈕功能
saveButton.addEventListener('click', () => {
const config = {
data: JSON.stringify(data)
}
kintone.plugin.app.setConfig(config)
})
如前所述,config
必須是以「鍵值對」為結構的物件,且值僅支援字串格式。因此,這裡我們使用 JSON.stringify()
將 data
陣列轉為字串,再存入 config
中。
由於此處省略了 successCallback
參數,設定儲存成功後會自動跳轉回應用程式的外掛清單畫面,並顯示設定完成的提示訊息。
下一步,我們希望在使用者再次打開外掛設定畫面時,自動載入先前儲存的設定。這時可透過 kintone.plugin.app.getConfig(pluginId)
來取得已保存的外掛資料:
// 預設空白列資料
const defaltRowData = {
table: '',
conditionField: '',
conditionValue: '',
inputField: '',
outputField: ''
}
let data = [defaltRowData]
// 載入已保存的設定資料
const pluginConfig = kintone.plugin.app.getConfig(PLUGIN_ID)
if (pluginConfig.data) {
const configData = JSON.parse(pluginConfig.data)
if (Array.isArray(configData) && configData.length > 0) {
data = configData
}
}
在既有的「預設空白列資料」邏輯下,我們透過 API 取得先前儲存的外掛設定資料 pluginConfig
。由於我們是把表格的資料存在 data
這個屬性中,並且是以 JSON 字串形式儲存,所以這裡需要用 JSON.parse(pluginConfig.data)
將它還原為陣列。
為了讓設定表格正確載入資料,我們加上條件判斷,確認 configData
是一個陣列且長度大於 0,才會覆蓋掉原本的 data
;如果沒有有效資料,則維持初始的空白列。
設定成功的話,重新進入外掛設定畫面時,就能看到表格自動載入剛才儲存的設定內容囉。
最後,來幫「取消」按鈕加上功能,讓使用者點擊後可以直接返回應用程式的外掛一覽畫面:
cancelButton.addEventListener('click', () => {
// 返回外掛程式一覽畫面
window.location.href = '../../' + kintone.app.getId() + '/plugin/'
})
到這裡為止,整個外掛的設定畫面就算是完成囉!
首先,將 customize.js
中原本的程式邏輯清除,只保留建立 change 事件所需的函式,並且記得在外層的 IIFE 中加上 PLUGIN_ID
參數與對應的引數。
接著,加入取得外掛設定的邏輯,並將設定資料暫存到 data
陣列中。這段程式碼可以直接從 config.js
中複製過來使用:
((PLUGIN_ID) => {
'use strict'
// 取得外掛設定資料
let data = []
const pluginConfig = kintone.plugin.app.getConfig(PLUGIN_ID)
if (pluginConfig.data) {
const configData = JSON.parse(pluginConfig.data)
if (Array.isArray(configData) && configData.length > 0) {
data = configData
}
}
// 建立 change events 用
function createChangeEvents(fieldCodes) {
if (!Array.isArray(fieldCodes)) return
return fieldCodes.map(fieldCode => [
`app.record.create.change.${fieldCode}`,
`app.record.edit.change.${fieldCode}`,
`mobile.app.record.create.change.${fieldCode}`,
`mobile.app.record.edit.change.${fieldCode}`,
`app.record.index.edit.change.${fieldCode}`
]).flat()
}
})(kintone.$PLUGIN_ID)
目前 data
的資料格式是以設定表格的形式儲存,每一筆設定對應一列資料。為了讓後續的處理更方便,我們可以先將這個陣列重新整理為物件的形式。以表格欄位代碼(data.table
的值)作為 key,將屬於同一表格的設定分組起來:
// 將各表格的設定資料重新整理
const tableSet = data.reduce((acc, item) => {
acc[item.table] = acc[item.table] || []
acc[item.table].push(item)
return acc
}, {})
這樣處理後,我們就能很方便地針對每一個表格個別進行處理。例如:tableSet['明細']
就會對應到屬於「明細」的所有設定資料,如下圖所示:
將各表格的設定資料分組整理完後,我們可以透過 JavaScript 的 Object.entries()
搭配 forEach()
,遍歷 tableSet
,針對每個表格動態註冊對應的事件處理邏輯:
// 動態建立 kintone events
Object.entries(tableSet).forEach(([tableField, settings]) => {
// 取出要監聽變化的表格中欄位(條件欄位、欲加總數值欄位)
const changeFields = [
...new Set(
settings.flatMap(setting => [setting.conditionField, setting.inputField])
),
]
// 取出小計欄位
const outputFields = settings.map(setting => setting.outputField)
// kintone events
kintone.events.on(
[
'app.record.create.show',
'app.record.edit.show',
'mobile.app.record.create.show',
'mobile.app.record.edit.show',
createChangeEvents([...changeFields, tableField]).flat(),
],
event => {
const { record } = event
const table = record[tableField].value
// 禁止編輯小計欄位
outputFields.forEach(field => {
if (record[field]) record[field].disabled = true
})
// 初始化 subtotal
const subtotal = Object.fromEntries(outputFields.map(field => [field, 0]))
// 動態加總邏輯
table.forEach(row => {
settings.forEach(setting => {
const category = row.value[setting.conditionField]?.value
const price = Number(row.value[setting.inputField]?.value) || 0
if (category === setting.conditionValue) {
subtotal[setting.outputField] += price
}
})
})
// 將小計值寫入記錄欄位
outputFields.forEach(field => {
record[field].value = subtotal[field]
})
return event
}
)
})
這樣的寫法可以針對每個子表格分別處理,加總邏輯也能根據各自的設定靈活執行。完成後的實際畫面如下所示:
最後,附上改寫後的 customize.js
完整程式碼供大家參考:
((PLUGIN_ID) => {
'use strict'
// 取得外掛設定資料
let data = []
const pluginConfig = kintone.plugin.app.getConfig(PLUGIN_ID)
if (pluginConfig.data) {
const configData = JSON.parse(pluginConfig.data)
if (Array.isArray(configData) && configData.length > 0) {
data = configData
}
}
// 將各表格的設定資料重新整理
const tableSet = data.reduce((acc, item) => {
acc[item.table] = acc[item.table] || []
acc[item.table].push(item)
return acc
}, {})
// 動態建立 kintone events
Object.entries(tableSet).forEach(([tableField, settings]) => {
// 取出要監聽變化的表格中欄位(條件欄位、欲加總數值欄位)
const changeFields = [...new Set(
settings.flatMap(setting => [setting.conditionField, setting.inputField])
)]
// 取出小計欄位
const outputFields = settings.map(setting => setting.outputField)
// kintone events
kintone.events.on([
'app.record.create.show', 'app.record.edit.show',
'mobile.app.record.create.show', 'mobile.app.record.edit.show',
createChangeEvents([...changeFields, tableField]).flat()
], event => {
const { record } = event
const table = record[tableField].value
// 禁止編輯小計欄位
outputFields.forEach(field => {
if (record[field]) record[field].disabled = true
})
// 初始化 subtotal
const subtotal = Object.fromEntries(outputFields.map(field => [field, 0]))
// 動態加總邏輯
table.forEach(row => {
settings.forEach(setting => {
const category = row.value[setting.conditionField]?.value
const price = Number(row.value[setting.inputField]?.value) || 0
if (category === setting.conditionValue) {
subtotal[setting.outputField] += price
}
})
})
// 將小計值寫入記錄欄位
outputFields.forEach(field => {
record[field].value = subtotal[field]
})
return event
})
})
// 建立 change events 用
function createChangeEvents(fieldCodes) {
if (!Array.isArray(fieldCodes)) return
return fieldCodes.map(fieldCode => [
`app.record.create.change.${fieldCode}`,
`app.record.edit.change.${fieldCode}`,
`mobile.app.record.create.change.${fieldCode}`,
`mobile.app.record.edit.change.${fieldCode}`,
`app.record.index.edit.change.${fieldCode}`
]).flat()
}
})(kintone.$PLUGIN_ID)
在本系列的「kintone 外掛開發」教學中,我們介紹了外掛的基本結構、打包所需的工具、kintone plugin API 的使用方式,並實際完成了一個具備設定畫面與運作邏輯的簡易外掛。
雖然篇幅有限,無法涵蓋所有進階技巧與 API,但希望透過這幾篇文章,能讓你對 kintone 外掛的開發流程有更清晰的認識,並作為進一步延伸的起點。若想了解更多細節與可能的擴充方向,建議可搭配官方文件深入參考,靈活運用這些工具與技術,打造更貼近需求的 kintone 解決方案!